#include "php_myserver.h"

zval *php_sw_server_callbacks[PHP_SERVER_CALLBACK_NUM];
zend_fcall_info_cache *php_sw_server_caches[PHP_SERVER_CALLBACK_NUM];
int php_swoole_onReceive(swServer *serv, swEventData *req);

PHP_METHOD(myserver_server,__construct){
	char *serv_host = NULL;
	size_t arg_len, len;
	long serv_port;

	zval *server_object = getThis();

	swServer *serv = malloc(sizeof(swServer));
	swServer_init(serv);

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &serv_host, &arg_len,&serv_port) == FAILURE) {
		return;
	}

	zend_update_property_long(myserver_server_class_entry_ptr,server_object,ZEND_STRL("port"),serv_port);
	zend_update_property_stringl(myserver_server_class_entry_ptr,server_object,ZEND_STRL("host"),serv_host,arg_len);

	if(SwooleG.main_reactor != NULL){
		RETURN_FALSE;
	}

	swListenPort *port = swServer_add_port(serv,serv_host,serv_port);

	swoole_set_object(server_object, serv);
}

PHP_METHOD(myserver_server,__destruct){
	printf("goodbye myserver!!!");
}

void php_swoole_server_before_start(swServer *serv,zval *zobject){
	if(swServer_create(serv) < 0){
		return ;
	}

	sw_zval_add_ref(&zobject);
	serv->ptr2 = sw_zval_dup(zobject);
}

int php_swoole_get_send_data(zval *zdata, char **str TSRMLS_DC){
	int length;

	convert_to_string(zdata);
	length = Z_STRLEN_P(zdata);
	*str = Z_STRVAL_P(zdata);

	return length;
}

PHP_METHOD(myserver_server,send){
	//only think of tcp:
	int ret;

	printf("=====start sending data======\n");

    zval *zfd;
    zval *zdata;

    swServer *serv = swoole_get_object(getThis());

	ZEND_PARSE_PARAMETERS_START(2, 2)
        Z_PARAM_ZVAL(zfd)
        Z_PARAM_ZVAL(zdata)
    ZEND_PARSE_PARAMETERS_END();

	char *data;
    int length = php_swoole_get_send_data(zdata, &data TSRMLS_CC);

	if(length < 0){
		RETURN_FALSE;
	}else if(length == 0){
		printf("data is empty\n");
		RETURN_FALSE;
	}

	convert_to_long(zfd);
	uint32_t fd = (uint32_t) Z_LVAL_P(zfd);

	if(swServer_tcp_send(serv, fd, data, length) < 0){
		RETURN_FALSE;
	}

	RETURN_TRUE;
}

PHP_METHOD(myserver_server, set){
	zval *zset = NULL;
    zval *zobject = getThis();
    HashTable *vht;

    zval *v;

    swServer *serv = swoole_get_object(zobject);

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zset) == FAILURE)
    {
        return;
    }
    if (Z_TYPE_P(zset) != IS_ARRAY)
    {
        RETURN_FALSE;
    }

	php_swoole_array_separate(zset);
    vht = Z_ARRVAL_P(zset);

	//reactor thread num
    if (php_swoole_array_get_value(vht, "reactor_num", v))
    {
        convert_to_long(v);
        serv->reactor_num = (int) Z_LVAL_P(v);
        if (serv->reactor_num <= 0)
        {
            serv->reactor_num = SwooleG.cpu_num;
        }
    }
    //worker_num
    if (php_swoole_array_get_value(vht, "worker_num", v))
    {
        convert_to_long(v);
        serv->worker_num = (int) Z_LVAL_P(v);
        if (serv->worker_num <= 0)
        {
            serv->worker_num = SwooleG.cpu_num;
        }
    }
	
	sw_zval_ptr_dtor(&zset);

	RETURN_TRUE;
}

PHP_METHOD(myserver_server,on){
	zval *name;
    zval *cb;

    swServer *serv = swoole_get_object(getThis());

	if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "zz", &name, &cb) == FAILURE)
    {
        return;
    }

	char *func_name = NULL;
    zend_fcall_info_cache *func_cache = emalloc(sizeof(zend_fcall_info_cache));
    if (!sw_zend_is_callable_ex(cb, NULL, 0, &func_name, NULL, func_cache, NULL TSRMLS_CC))
    {
        efree(func_name);
        return;
    }
    efree(func_name);

	convert_to_string(name);

	char *callback_name[PHP_SERVER_CALLBACK_NUM] = {
        "Connect",
        "Receive",
        "Close",
        "Packet",
        "Start",
        "Shutdown",
        "WorkerStart",
        "WorkerStop",
        "Task",
        "Finish",
        "WorkerExit",
        "WorkerError",
        "ManagerStart",
        "ManagerStop",
        "PipeMessage",
        NULL,
        NULL,
        NULL,
        NULL,
        "BufferFull",
        "BufferEmpty",
    };

	int i;
	char property_name[128];
	int l_property_name = 0;
	memcpy(property_name, "on", 2);

	for (i = 0; i < PHP_SERVER_CALLBACK_NUM; i++)
    {
        if (callback_name[i] == NULL)
        {
            continue;
        }
        if (strncasecmp(callback_name[i], Z_STRVAL_P(name), Z_STRLEN_P(name)) == 0)
        {
            memcpy(property_name + 2, callback_name[i], Z_STRLEN_P(name));
            l_property_name = Z_STRLEN_P(name) + 2;
            property_name[l_property_name] = '\0';
            zend_update_property(myserver_server_class_entry_ptr, getThis(), property_name, l_property_name, cb TSRMLS_CC);
            php_sw_server_callbacks[i] = sw_zend_read_property(myserver_server_class_entry_ptr, getThis(), property_name, l_property_name, 0 TSRMLS_CC);
            php_sw_server_caches[i] = func_cache;
			// i do not know why i need this code!!!!
            //sw_copy_to_stack(php_sw_server_callbacks[i], _php_sw_server_callbacks[i]);

			if (i == SW_SERVER_CB_onConnect && serv->onConnect == NULL)
            {
                serv->onConnect = php_swoole_onConnect;
            }
            else if (i == SW_SERVER_CB_onClose && serv->onClose == NULL)
            {
                serv->onClose = php_swoole_onClose;
            }
            break;
        }
    }

	//do not think of multil ports
}

PHP_METHOD(myserver_server,start){
	zval *zobject = getThis();
    int ret;

	swServer *serv = swoole_get_object(getThis());

	if (php_sw_server_callbacks[SW_SERVER_CB_onReceive] == NULL && php_sw_server_callbacks[SW_SERVER_CB_onPacket] == NULL)
    {
        printf("require onReceive/onPacket callback\n");
        RETURN_FALSE;
    }
	serv->onReceive = php_swoole_onReceive;

	php_swoole_server_before_start(serv,zobject);
	ret = swServer_start(serv);

	printf("run start !!!\n");

	RETURN_TRUE;
}

void php_swoole_onConnect(swServer *serv, swDataHead *info){
	zval *zserv = (zval *) serv->ptr2;
    zval *zfd;
    zval *zfrom_id;
    zval **args[3];
    zval *retval = NULL;

    SWOOLE_GET_TSRMLS;

    zval *callback = php_swoole_server_get_callback(serv, info->from_fd, SW_SERVER_CB_onConnect);
    if (callback == NULL || ZVAL_IS_NULL(callback))
    {
        return;
    }

    SW_MAKE_STD_ZVAL(zfd);
    ZVAL_LONG(zfd, info->fd);

    SW_MAKE_STD_ZVAL(zfrom_id);
    ZVAL_LONG(zfrom_id, info->from_id);

    args[0] = &zserv;
    args[1] = &zfd;
    args[2] = &zfrom_id;

    zend_fcall_info_cache *fci_cache = php_swoole_server_get_cache(serv, info->from_fd, SW_SERVER_CB_onConnect);
    if (sw_call_user_function_fast(callback, fci_cache, &retval, 3, args TSRMLS_CC) == FAILURE)
    {
		printf("onconnect handler error\n");
    }
    if (EG(exception))
    {
        zend_exception_error(EG(exception), E_ERROR TSRMLS_CC);
    }

    sw_zval_ptr_dtor(&zfd);
    sw_zval_ptr_dtor(&zfrom_id);
    if (retval != NULL)
    {
        sw_zval_ptr_dtor(&retval);
    }
}

void php_swoole_onClose(swServer *serv, swDataHead *info){
	zval *zserv = (zval *) serv->ptr2;
    zval *zfd;
    zval *zfrom_id;
    zval **args[3];
    zval *retval = NULL;

	zval *callback = php_swoole_server_get_callback(serv, info->from_fd, SW_SERVER_CB_onClose);
    if (callback == NULL || ZVAL_IS_NULL(callback))
    {
        return;
    }

	SW_MAKE_STD_ZVAL(zfd);
    ZVAL_LONG(zfd, info->fd);

    SW_MAKE_STD_ZVAL(zfrom_id);
    ZVAL_LONG(zfrom_id, info->from_id);

    args[0] = &zserv;
    args[1] = &zfd;
    args[2] = &zfrom_id;

	zend_fcall_info_cache *fci_cache = php_swoole_server_get_cache(serv, info->from_fd, SW_SERVER_CB_onClose);
    if (sw_call_user_function_fast(callback, fci_cache, &retval, 3, args TSRMLS_CC) == FAILURE){
		printf("onClose handler error.\n");
	}

	if (EG(exception))
    {
        zend_exception_error(EG(exception), E_ERROR TSRMLS_CC);
    }
    sw_zval_ptr_dtor(&zfd);
    sw_zval_ptr_dtor(&zfrom_id);
    if (retval != NULL)
    {
        sw_zval_ptr_dtor(&retval);
    }
}

void php_swoole_get_recv_data(zval *zdata, swEventData *req, char *header, uint32_t header_length){
	char *data_ptr = NULL;
    int data_len;

	data_ptr = req->data;
	data_len = req->info.len;

	SW_ZVAL_STRINGL(zdata, data_ptr + header_length, data_len - header_length, 1);
}

int php_swoole_onReceive(swServer *serv, swEventData *req){
	//only think of tcp 
	swFactory *factory = &serv->factory;
    zval *zserv = (zval *) serv->ptr2;
    zval **args[4];

    zval *zfd;
    zval *zfrom_id;
    zval *zdata;
    zval *retval = NULL;

	zval *callback = php_swoole_server_get_callback(serv, req->info.from_fd, SW_SERVER_CB_onReceive);
    if (callback == NULL || ZVAL_IS_NULL(callback))
    {
        printf("onReceive callback is null.");
        return 0;
    }

	SW_MAKE_STD_ZVAL(zfd);
    SW_MAKE_STD_ZVAL(zfrom_id);
    SW_MAKE_STD_ZVAL(zdata);

	ZVAL_LONG(zfrom_id, (long ) req->info.from_id);
	ZVAL_LONG(zfd, (long ) req->info.fd);
	php_swoole_get_recv_data(zdata, req, NULL, 0);

	args[0] = &zserv;
    args[1] = &zfd;
    args[2] = &zfrom_id;
    args[3] = &zdata;

	zend_fcall_info_cache *fci_cache = php_swoole_server_get_cache(serv, req->info.from_fd, SW_SERVER_CB_onReceive);
    if (sw_call_user_function_fast(callback, fci_cache, &retval, 4, args TSRMLS_CC) == FAILURE){
		printf("onReceive handler error.\n");
	}

	sw_zval_ptr_dtor(&zfd);
    sw_zval_ptr_dtor(&zfrom_id);
    sw_zval_ptr_dtor(&zdata);
    if (retval != NULL)
    {
        sw_zval_ptr_dtor(&retval);
    }

	return 0;
}
